package com.tiens.annotation.processor;

import com.alibaba.android.arouter.facade.annotation.Route;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.tiens.annotation.Login;
import com.tiens.annotation.TestCode;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;

@AutoService(Processor.class)
public class TiensProcessor extends AbstractProcessor {

    private Filer mFilerUtils;
    private Elements mElementUtils;
    private Messager mMessager;
    private LinkedHashSet<String> annotationTypes;
    private List<String> classList = new ArrayList<>();
    private String fullClassName;
    private Map<String, String> mOption;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mFilerUtils = processingEnv.getFiler();
        mElementUtils = processingEnv.getElementUtils();
        mMessager = processingEnv.getMessager();
        mOption = processingEnv.getOptions();
    }
    @Override
    public Set<String> getSupportedAnnotationTypes() {
       annotationTypes = new LinkedHashSet<>();
        annotationTypes.add(TestCode.class.getCanonicalName());
        annotationTypes.add(Login.class.getCanonicalName());
        return annotationTypes;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        for (String clsName :
                annotationTypes) {
            try {
                Class<? extends Annotation> cls = (Class<? extends Annotation>) Class.forName(clsName);
                if (TestCode.class.getCanonicalName().equals(cls.getCanonicalName())) {
                    return processTestCode(roundEnvironment, cls);
                }else if (Login.class.getCanonicalName().equals(cls.getCanonicalName())){
                    return processLogin(roundEnvironment,cls);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        return false;
    }

    private boolean processLogin(RoundEnvironment roundEnvironment, Class<? extends Annotation> cls) {
        mMessager.printMessage(Diagnostic.Kind.NOTE, "processing...");
        classList.clear();
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Login.class);

        for(Element element: elements) {
            TypeElement variableElement = (TypeElement) element;
            fullClassName = variableElement.getQualifiedName().toString();
            mMessager.printMessage(Diagnostic.Kind.NOTE, fullClassName);
            Route routeAnnotation = variableElement.getAnnotation(Route.class);
            String path = routeAnnotation.path();
            classList.add(path);
        }

        ClassName stringType = ClassName.get("java.lang", "String");
        ClassName list = ClassName.get("java.util", "ArrayList");
        TypeName listOfString = ParameterizedTypeName.get(list, stringType);

        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("getPathList")
                .addModifiers(Modifier.PUBLIC)
                .addModifiers(Modifier.STATIC)
                .returns(listOfString)
                .addStatement("ArrayList<String> pathList = new ArrayList<>()");

        for(String name : classList) {
            methodBuilder.addStatement("pathList.add($S)",name);
        }

        methodBuilder.addStatement("return pathList");
        Map<String,String> options = mOption;
        TypeSpec typeSpec = TypeSpec.classBuilder("PathUtil$"+options.get("moduleName"))
                .addModifiers(Modifier.PUBLIC)
                .addMethod(methodBuilder.build())
                .build();
        try {
            JavaFile.builder("com.tiens.vshare",typeSpec).build().writeTo(mFilerUtils);
        }catch (IOException e) {
            e.printStackTrace();
        }
        mMessager.printMessage(Diagnostic.Kind.NOTE, "process finish ...");
        return true;
    }

    private boolean processTestCode(RoundEnvironment roundEnvironment, Class<? extends Annotation> clz) {
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(clz);
        if (elements.size()>0){
            for (Element element :
                    elements) {
                TestCode testCode = element.getAnnotation(TestCode.class);
                if ("release".equals(testCode.variant()))
                    mMessager.printMessage(Diagnostic.Kind.ERROR,testCode.author()+"_"+testCode.describe(),element);
            }
        }
        return true;
    }
}
